// -----------------------------------------------------------------------------
// This source file is part of Matrix Platform
// 	(Universal .NET Software Development Platform)
// For the latest info, see http://www.matrixplatform.com
// 
// Copyright (c) 2009-2010, Ingenious Ltd
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// -----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace Matrix.Framework.DataStorage.DataSeries
{
    /// <summary>
    /// Important, make sure to keep this helper lockless, since it is used inside of owner intensive locks.
    /// </summary>
    public class DataSeriesSequenceHelper
    {
        public byte[] EscapeSequence { get; protected set; }

        byte _lastSeparatorByte = 0;

        /// <summary>
        /// Finishing byte is advised to be big.
        /// </summary>
        public readonly static byte[] DefaultEscapeSequence = new byte[] { 93, 181, 12, 249 };

        /// <summary>
        /// Constructor.
        /// </summary>
        public DataSeriesSequenceHelper(byte[] escapeSequence)
        {
            EscapeSequence = escapeSequence;
            _lastSeparatorByte = EscapeSequence[EscapeSequence.Length - 1];
        }

        /// <summary>
        /// 
        /// </summary>
        public void Dispose()
        {
        }

        ///// <summary>
        ///// 
        ///// </summary>
        //public byte[] FormatData(byte[] buffer, ref long bufferLength)
        //{
        //    return EscapeBuffer(buffer, ref bufferLength);
        //}

        /// <summary>
        /// 
        /// </summary>
        /// <param name="buffer"></param>
        /// <returns>Null if sequence not found in buffer.</returns>
        public List<long> GetEscapeSequencePositions(byte[] buffer, long startPosition, long bufferLength)
        {
            List<long> escapePositionsRaw = null;
            
            for (long position = startPosition; position < bufferLength; position++)
            {
                if (position >= EscapeSequence.Length - 1 && buffer[position] == _lastSeparatorByte)
                {// So we have a possible hit, check the previous bytes as well.
                    bool matched = true;
                    for (int j = 0; j < EscapeSequence.Length; j++)
                    {
                        if (buffer[position - j] != EscapeSequence[EscapeSequence.Length - j - 1])
                        {
                            matched = false;
                            break;
                        }
                    }

                    if (matched)
                    {// Matched an escape sequence.
                        if (escapePositionsRaw == null)
                        {
                            escapePositionsRaw = new List<long>();
                        }

                        //if (escapePositionsFiltered == null)
                        //{
                        //    escapePositionsFiltered = new List<long>();
                        //}

                        long value = position - EscapeSequence.Length + 1;
                        escapePositionsRaw.Add(value);

                        //if (escapePositionsRaw.Count < 2 ||
                        //    escapePositionsRaw[escapePositionsRaw.Count - 2] + EscapeSequence.Length < value)
                        //{
                        //    escapePositionsFiltered.Add(position - EscapeSequence.Length + 1);
                        //}
                        //else
                        //{// Skip the current one, and also pop the last one.
                        //    escapePositionsFiltered.RemoveAt(escapePositionsFiltered.Count - 1);
                        //}
                    }
                }
            }

            //// We can establish actual positions, based on the knowledge that each 
            //// item starter escape sequence is not followed by another one like it.
            //if (escapePositionsRaw != null)
            //{
            //    escapePositionsFiltered = new List<long>();

            //    for (int i = 0; i < escapePositionsRaw.Count; i++)
            //    {
            //        if (i < escapePositionsRaw[i])
            //        {
            //        }
            //    }
            //}

            return escapePositionsRaw;
        }

        protected void CopyArray(byte[] sourceBuffer, MemoryStream target, long from, long to)
        {
            if (from >= to)
            {
                return;
            }

            target.Write(sourceBuffer, (int)from, (int)(to - from));
        }

        /// <summary>
        /// Make sure to always execute when no other operations are done on the stream.
        /// </summary>
        public byte[] EscapeBuffer(byte[] buffer, ref long bufferLength)
        {
            List<long> escapesRaw = GetEscapeSequencePositions(buffer, 0, bufferLength);

            if (escapesRaw == null)
            {// Stream is clear, write directly.
                return buffer;
            }
        
            // There are escape sequences in data, need to replace them as we go.
            MemoryStream result = new MemoryStream();

            for (int i = 0; i < escapesRaw.Count; i++)
            {
                if (i == 0)
                {
                    CopyArray(buffer, result, 0, escapesRaw[i]);
                }

                result.Write(EscapeSequence, 0, EscapeSequence.Length);

                if (i == escapesRaw.Count - 1)
                {
                    CopyArray(buffer, result, escapesRaw[i], bufferLength);
                }
                else
                {
                    CopyArray(buffer, result, escapesRaw[i], escapesRaw[i + 1]);
                }
            }

            bufferLength = result.Length;
            return result.GetBuffer();
        }

        /// <summary>
        /// 
        /// </summary>
        public static int CompareBuffers(byte[] buffer1, long length1, byte[] buffer2, long length2)
        {
            if (length1 > length2)
            {
                return 1;
            }
            else if (length1 < length2)
            {
                return -1;
            }

            for (int i = 0; i < length1; i++)
            {
                if (buffer1[i] != buffer2[i])
                {
                    return buffer1[i].CompareTo(buffer2[2]);
                }
            }

            return 0;
        }

    }
}
